package com.tgy.栈;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/***
 * @ClassName: Calculator
 * @Description: 栈实现表达式计算器
 * @Auther: tanggy3
 * @Date: 20:51 2022/7/28
 * @version : V1.0
 */
public class Calculator {
    public static void main(String[] args) {
        System.out.println(deal("30/4+100+1+0+1000/10/2*3-1+0.5"));
    }

    /**
     * 双括号初始化
     */
    private static final Map<Character, Integer> map = new HashMap<Character, Integer>() {
        {
            put('+', 1);
            put('-', 1);
            put('*', 2);
            put('/', 2);

        }
    };


    /**
     * 计算两个运算符号的优先级
     * 前面的先算返回true
     *
     * @param a 运算符号a
     * @param b 运算符号b
     * @return
     */
    public static boolean getSort(char a, char b) {
        return map.get(a) > map.get(b);
    }

    /**
     * @param num1 计算数字一
     * @param num2 计算数字二
     * @param oper 计算符号
     * @return
     */
    public static BigDecimal compute(BigDecimal num1, BigDecimal num2, char oper) {
        BigDecimal res = new BigDecimal(0);// res 用于存放计算的结果
        switch (oper) {
            case '+':
                res = num1.add(num2);
                break;
            case '-':
                res = num2.subtract(num1);// 注意顺序
                break;
            case '*':
                res = num1.multiply(num2);
                break;
            case '/':
                res = num2.divide(num1);
                break;
            default:
                break;
        }
        return res;
    }


    /**
     * 判断字符是否是符号
     *
     * @param oper 字符
     * @return
     */
    public static boolean isOper(char oper) {
        return oper == '+' || oper == '-' || oper == '*' || oper == '/';
    }

    /**
     * 计算器
     * 3+2*6-2
     */
    public static BigDecimal deal(String text) {
        //定义需要的相关变量
        int index = 0;//用于扫描
        BigDecimal num1;
        BigDecimal num2;
        char oper = ' ';
        BigDecimal res;
        Character ch = ' '; //将每次扫描得到char保存到ch
        String keepNum = ""; //用于拼接 多位数
        Stack<BigDecimal> intStack = new Stack<BigDecimal>();
        Stack<Character> operStack = new Stack<Character>();

        //开始while循环的扫描expression
        while (true) {
            ch = text.substring(index, index + 1).charAt(0);
            index++;
            if (isOper(ch)) {//如果是符号
                //判断当前的符号栈是否为空
                if (!operStack.isEmpty()) {
                    //如果符号栈有操作符，就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,
                    //在从符号栈中pop出一个符号，进行运算，将得到结果，入数栈，然后将当前的操作符入符号栈
                    if (!getSort(ch, operStack.peek())) {//在栈中的优先
                        BigDecimal compute = compute(intStack.pop(), intStack.pop(), operStack.pop());
                        intStack.push(compute);
                        operStack.push(ch);
                    } else {//来的优先
                        operStack.push(ch);
                    }
                } else {
                    operStack.push(ch);
                }
            } else {//如果是数字
                StringBuffer stringBuffer = new StringBuffer();
                stringBuffer.append(ch);
                /**
                 * 当index小于text的长度一直做计算
                 */
                while (index < text.length()) {
                    //当 当前的char的下一个char不为运算符的时候 一直循环添加到stringBuffer
                    while ((!isOper(text.substring(index, index + 1).charAt(0)))) {
                        stringBuffer.append(text.substring(index, index + 1).charAt(0));
                        index++;//取出字符后索引加一！！！
                        if (index == text.length()) break;//当字符为最后一个字符时候退出
                    }
                    break;
                }
                intStack.push(new BigDecimal(Float.valueOf(stringBuffer.toString())));
            }
            //遍历结束，推出循环
            if (index == text.length()) break;
        }

        //当表达式扫描完毕，就顺序的从 数栈和符号栈中pop出相应的数和符号，并运行.
        while (true) {
            //如果符号栈为空，则计算到最后的结果, 数栈中只有一个数字【结果】
            if (operStack.isEmpty()) {
                break;
            }
            num1 = intStack.pop();
            num2 = intStack.pop();
            oper = operStack.pop();
            res = compute(num1, num2, oper);
            intStack.push(res);//入栈
        }
        //将数栈的最后数，pop出，就是结果
        BigDecimal res2 = intStack.pop();
        System.out.printf("表达式 %s =" + res2 + "\n", text);
        return res2;
    }

};

